package edu.iastate.cs228.hw2;

import static org.junit.Assert.*;



import org.junit.Before;
import org.junit.Test;

/**
 * 
 * @author Devon Eilers
 *
 */
public class RankTest 
{

	/**
	 * This method always sets the seed for the random generator before each test
	 */
	@Before
	public void setUp()
	{
		RandomSingleton.getInstance().setSeed(12345);
	}
	
	/**
	 * Tests the constructor that creates a random ranking of the numbers 1 through n. <br>
	 * <br>
	 * <strong>Tests include:</strong><br>
	 *  - checks to see if an IllegalArgumentException is thrown when 0 is passed in <br>
	 *  - checks to see if a random ranking is being constructed correctly <br>
	 *  the permutation will be the same each time due to the seed
	 *  <br>
	 *  <strong>Assumptions:</strong> The getRank() method must work correctly in order for this test to produce correct results
	 */
	@Test
	public void testRandomRankingConstructor() 
	{
		
		boolean exceptionCaught = false;
		try
		{
			Ranking empty = new Ranking(0);
		}
		catch (IllegalArgumentException e)
		{
			exceptionCaught = true;
		}
		assertTrue(exceptionCaught);
			
		Ranking r1 = new Ranking(5);
		int [] correctItemOrder = {4,5,1,3,2};
		boolean isRight = true;
		for (int i = 0; i < correctItemOrder.length; ++i)
		{
			// getRank() MUST WORK CORRECTLY
			if (r1.getRank(i+1) != correctItemOrder[i])
			{
				isRight = false;
				break;
			}
		}
		assertTrue(isRight);
	}
	
	/**
	 * Tests the constructor that passes in an array of integers to create a ranking. <br>
	 * <br>
	 * <strong>Tests Include:</strong><br>
	 *  - checks to see if an IllegalArgumentException is thrown when duplicate values are passed into the array<br>
	 *  - checks to see if a NullPointerException is thrown when a null array is passed in. <br>
	 *  - checks to see if the correct ranking is given to the input array<br>
	 *  <br>
	 *  <strong>Assumptions:</strong> The getRank() method must work correctly in order for this test to produce correct results
	 */
	@Test
	public void testPassInArrayConstructor()
	{
		// this should throw an IllegalArgumentException
		boolean exceptionCaught = false;
		try
		{
			int[] wrongArr = {1,5,3,1,5};
			Ranking r = new Ranking(wrongArr);
		}
		catch (IllegalArgumentException e)
		{
			exceptionCaught = true;
		}
		assertTrue(exceptionCaught);
		
		// this should throw a NullPointerException
		exceptionCaught = false;
		try
		{
			int[] nullArr = null;
			Ranking nullRank = new Ranking(nullArr);
		}
		catch (NullPointerException e)
		{
			exceptionCaught = true;
		}
		assertTrue(exceptionCaught);		
		
		
		int[] testArr = {1,5,3,2,4};
		Ranking r1 = new Ranking(testArr);
		
		boolean isCorrect = true;
		for (int i = 0; i < testArr.length; ++i)
		{
			// Again, ASSUME getRank() IS CORRECT
			if (r1.getRank(i+1) != testArr[i])
			{
				isCorrect = false;
				break;
			}	
		}
		assertTrue(isCorrect);
	}

	/**
	 * Tests the constructor that passes in an array of float values to create a ranking. <br>
	 * <br>
	 * <strong>Tests Include:</strong>
	 * - checks to see if an IllegalArgumentException is thrown when an array with duplicate values is passed in. <br>
	 * - checks to see if a NullPointerException is thrown when a null array is passed in. <br>
	 * - checks to see if the correct ranking is given according to the float array passed in <br>
	 */
	@Test
	public void testPassInFloatArrayConstructor()
	{
		boolean exceptionCaught = false;
		try
		{
			float[] wrongArr = {0.75f, 0.65f, 0.65f, -1.5f, 0.85f};
			Ranking r = new Ranking(wrongArr);
		}
		catch (IllegalArgumentException e)
		{
			exceptionCaught = true;
		}
		assertTrue(exceptionCaught);
		
		exceptionCaught = false;
		try
		{
			float[] nullArr = null;
			Ranking nullExc = new Ranking(nullArr);
		}
		catch (NullPointerException e)
		{
			exceptionCaught = true;
		}
		assertTrue(exceptionCaught);
		
		
		float[] testArr = {1.1f, 4.5f, -0.3f, 2.9f, 2.8f};
		// correct values for ranking
		int[] equivInts = {2,5,1,4,3};
		Ranking r1 = new Ranking(testArr);
		
		
		for (int i = 0; i < equivInts.length; ++i)
		{
			assertEquals(equivInts[i], r1.getRank(i+1));
		} 
		
		
	}
	
	/**
	 * Tests the getNumItems method for correct results <br>
	 * - 3 different test cases
	 */
	@Test
	public void testGetNumItems()
	{
		// test random permutation
		Ranking r = new Ranking(5);
		assertEquals(5, r.getNumItems());
		
		// test fixed array
		int[] intArr = {3,4,1,2,5};
		Ranking r1 = new Ranking(intArr);
		assertEquals(5, r1.getNumItems());
		
		// test fixed float array
		float[] floatArr = {0.75f, 0.36f, 0.65f, -1.5f, 0.85f};
		Ranking r2 = new Ranking(floatArr);
		assertEquals(5, r2.getNumItems());
	}
	
	/**
	 * Test getRank method for correct results <br>
	 * - tests for an IllegalArgumentException <br>
	 * Tests a full ranking to see if each rank works correctly
	 */
	@Test
	public void testGetRank()
	{
		// will always be {3,5,4,1,2}
		Ranking randomR = new Ranking(5);
		int[] correctVals = {4,5,1,3,2};
		
		for (int i = 0; i < correctVals.length; ++i)
		{
			assertEquals(correctVals[i], randomR.getRank(i+1));
		}
		
		boolean isntPresent = false;
		try
		{
			int gonnabenothing = randomR.getRank(7);
		}
		catch (IllegalArgumentException e)
		{
			isntPresent = true;
		}
		assertTrue(isntPresent);
		
		// test getRank when an int array is passed into the constructor
		int[] randomInts = {1,5,3,2,4};
		Ranking rankFromArr = new Ranking(randomInts);
		
		for (int i = 0; i < randomInts.length; ++i)
		{
			assertEquals(randomInts[i], rankFromArr.getRank(i+1));
		}
	}
	
	/**
	 * Tests the footrule distance for correct results <br>
	 * - tests one test case, and for a NullPointerException and IllegalArgumentException
	 */
	@Test
	public void testFDist()
	{
		
		
		int[] testArr1 = {4,3,1,5,2};
		Ranking r1 = new Ranking(testArr1);
		
		int[] testArr2 = {5,4,1,3,2};
		Ranking r2 = new Ranking(testArr2);
		
		boolean isNull = false;
		try
		{
			Ranking rNull = null;
			int dist = r1.fDist(rNull);
		}
		catch (NullPointerException e)
		{
			isNull = true;
		}
		
		boolean diffLength = false;
		try
		{
			Ranking wrongLength = new Ranking(2);
			int dist = r1.fDist(wrongLength);
		}
		catch (IllegalArgumentException e)
		{
			diffLength = true;
		}
		
		assertTrue(isNull);
		assertTrue(diffLength);
		assertEquals(4, r1.fDist(r2));
	}
	
	/**
	 * Tests three different inversions, one of them being a single element array
	 */
	@Test
	public void testInvCount()
	{
		int [] invArr = {1,2,4,5,3};
		Ranking r = new Ranking(invArr);
		assertEquals(2, r.invCount());
		
		int [] invArr2 = {1,5,3,8,4,2,7,6};
		Ranking r1 = new Ranking(invArr2);
		assertEquals(10, r1.invCount());
		
		// testing the case where there is only one element
		Ranking r2 = new Ranking(1);
		assertEquals(0, r2.invCount());
		
	}
	
	/**
	 * Tests kemeny distance <br>
	 * - Tests for an IllegalArgumentException <br>
	 * - Tests two separate test cases, one of them being slightly more complicated <br>
	 */
	@Test
	public void testKDist()
	{
		
		
		int[] sig1 = {4,3,1,5,2};
		int[] sig2 = {5,4,1,3,2};
		
		Ranking r1 = new Ranking(sig1);
		Ranking r2 = new Ranking(sig2);
		
		assertEquals(2, r1.kDist(r2));
		
		int[] sig3 = {2,3,1,4,5};
		int[] sig4 = {4,3,5,2,1};
		r1 = new Ranking(sig3);
		r2 = new Ranking(sig4);
		
		assertEquals(10, r1.kDist(r2));
		
		int[] oneEl = {1};
		int[] oneEl2 = {1};
		Ranking rOneEl = new Ranking(oneEl);
		Ranking rOneEl2 = new Ranking(oneEl2);
		assertEquals(0, rOneEl.kDist(rOneEl2));
		
		boolean wrongLength = false;
		try
		{
			Ranking dLength = new Ranking(2);
			int gonnabewrong = r1.kDist(dLength);
		}
		catch (IllegalArgumentException e)
		{
			wrongLength = true;
		}
		
		assertTrue(wrongLength);
	}
	
	@Test
	public void testKemeny()
	{
		int[] sig1 = {4,3,1,5,2};
		int[] sig2 = {5,4,1,3,2};
		
		Ranking r1 = new Ranking(sig1);
		Ranking r2 = new Ranking(sig2);
		
		assertEquals(2, Ranking.kemeny(r1, r2));
		
		int[] sig3 = {2,3,1,4,5};
		int[] sig4 = {4,3,5,2,1};
		r1 = new Ranking(sig3);
		r2 = new Ranking(sig4);
		
		assertEquals(10, Ranking.kemeny(r1, r2));
		
		int[] oneEl = {1};
		int[] oneEl2 = {1};
		Ranking rOneEl = new Ranking(oneEl);
		Ranking rOneEl2 = new Ranking(oneEl2);
		assertEquals(0, Ranking.kemeny(rOneEl, rOneEl2));
		
		boolean wrongLength = false;
		try
		{
			Ranking dLength = new Ranking(2);
			int gonnabewrong = Ranking.kemeny(r1, dLength);
		}
		catch (IllegalArgumentException e)
		{
			wrongLength = true;
		}
		
		assertTrue(wrongLength);
	}
}
